 
#include <stdio.h>

#include "minunit.h"

#include <multifit/gsl_multifit.h>
#include <test/gsl_test.h>
#include <matrix/gsl_matrix.h>
#include <vector/gsl_vector.h>

#include "ddas_client_io.h"
#include "ddas_client_setup.h"
#include "ddas_globals.h"

int tests_run = 0;

char test_errors[4096];
char assertion_errors[512];

 
//purpose
static char * test_linearfit() 
{

int i, n, p;

double xi, yi, ei, chisq;

gsl_multifit_linear_workspace * work;
gsl_matrix *X, *cov;
gsl_vector *y, *w, *c;


p = 4; //we just know this based on the example above
//note that p=3+1, where the one is for a column of 1s for the intercept coefficient
n = 5; //we just know this based on the example above

// allocate space for the matrices and vectors
X = gsl_matrix_alloc(n, p); // this is an input
y = gsl_vector_alloc(n); //this is an input

c = gsl_vector_alloc(p); //this is an output
cov = gsl_matrix_alloc(p, p); //this is an output

//now put the data into the X matrix, row by row
gsl_matrix_set(X, 0, 0, 1); // this is the constant of one for each
gsl_matrix_set(X, 0, 1, 1);
gsl_matrix_set(X, 0, 2, 25);
gsl_matrix_set(X, 0, 3, 72); //first row, i.e., Joe, done

gsl_matrix_set(X, 1, 0, 1);
gsl_matrix_set(X, 1, 1, 0);
gsl_matrix_set(X, 1, 2, 32);
gsl_matrix_set(X, 1, 3, 68); // Jill, done

gsl_matrix_set(X, 2, 0, 1);
gsl_matrix_set(X, 2, 1, 1);
gsl_matrix_set(X, 2, 2, 27);
gsl_matrix_set(X, 2, 3, 69); // Jack done

gsl_matrix_set(X, 3, 0, 1);
gsl_matrix_set(X, 3, 1, 1);
gsl_matrix_set(X, 3, 2, 45);
gsl_matrix_set(X, 3, 3, 67); // John done

gsl_matrix_set(X, 4, 0, 1);
gsl_matrix_set(X, 4, 1, 0);
gsl_matrix_set(X, 4, 2, 38);
gsl_matrix_set(X, 4, 3, 62); // Jane done

//now enter the dependent data, y, representing weight

gsl_vector_set(y, 0, 178); // Joe's weight
gsl_vector_set(y, 1, 122); // Jill's weight
gsl_vector_set(y, 2, 167); // Jack
gsl_vector_set(y, 3, 210); // John
gsl_vector_set(y, 4, 108); // Jane

//now make some calls to gsl

// allocate temporary work space for gsl 

work = gsl_multifit_linear_alloc(n, p);

// now do the fit
gsl_multifit_linear (X, y, c, cov, &chisq, work);

double expectedc0, expectedc1, expectedc2, expectedc3;
expectedc0= -337.9447029;
expectedc1= 54.63010021;
expectedc2= 2.965282749;
expectedc3= 5.371689334;


//this should succeed
mu_assert("intercept coefficient", mu_equal_abs_tolerance(expectedc0,gsl_vector_get(c,0),0.01));
mu_assert("coefficient 1", mu_equal_abs_tolerance(expectedc1,gsl_vector_get(c,1),0.01));
mu_assert("coefficient 2", mu_equal_abs_tolerance(expectedc2,gsl_vector_get(c,2),0.01));
mu_assert("coefficient 3", mu_equal_abs_tolerance(expectedc3,gsl_vector_get(c,3),0.01));
	

/* some notes are in order here:
X and y are inputs, c, cov, and chisq are outputs
c is a p-length vector with the parameters that minimized the prediction error
cov is a pxp matrix with a covariance matrix for the independent variable
of particular note in cov is the diagonal element (i,i), which is the variance of 
  the ith independent variable. In our example, the cov(1,1) is the variance of the ages
chisq is the sum-of-squared error for this model (lower is better)
work is merely a workspace that we provide to gsl
*/

//now do what ever we want with the results 
//most likely we will dump results to a text file

//************************************
//very important stuff at the end here
//************************************
// don't forget to deallocate - there is no garbage collector in C!

	gsl_matrix_free(X);
	gsl_matrix_free(cov);
	gsl_vector_free(y);
	gsl_vector_free(c);


	return 0;
}
	


static char * test_relative_tolerance()
{
	double rel_tol = 0.00001;

	mu_assert( "error in unit testing framework relative tolerance 1", mu_equal_abs_tolerance(5.0, 5.000009,rel_tol) );
	mu_assert( "error in unit testing framework relative tolerance 2", mu_equal_abs_tolerance(0.0, 0.0000099,rel_tol) );
	mu_assert( "error in unit testing framework relative tolerance 3", 0==mu_equal_abs_tolerance(5.0, 5.000011,rel_tol) );
	
	cout<<"SUCCESS: test_relative_tolerance"<<endl;

	return 0;
}

/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_data_file_parse()
{
	//required before calling data file
	ddas_parse_scoping_file();

	//parse data file
	ddas_parse_data_file();

	//check that 'master' matrix data are correct
	mu_assert( "test_data_file_parse: assertion #1 failed", gsl_matrix_get( master, 0, 0 )	 ==6.0);
	mu_assert( "test_data_file_parse: assertion #2 failed", gsl_matrix_get( master, 2, 2 )   ==64.0);
	mu_assert( "test_data_file_parse: assertion #3 failed", gsl_matrix_get( master, 4, 5 )   ==43.1);
	mu_assert( "test_data_file_parse: assertion #4 failed", gsl_matrix_get( master, 767, 14 )==0.315);

	cout<<"SUCCESS: test_data_file_parse"<<endl;

	//success
	return 0;
}


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_independent_matrix()
{
	//required before calling data file
	ddas_parse_scoping_file();

	//parse data file
	ddas_parse_data_file();

	//using 'master' populate 'independent' matrix
	ddas_populate_independent_matrix();

	//check that independent matrix is correct
	mu_assert( "test_independent_matrix: assertion #1 failed", gsl_matrix_get( independent, 0, 0 )	  ==6.0);
	mu_assert( "test_independent_matrix: assertion #2 failed", gsl_matrix_get( independent, 2, 2 )   ==64.0);
	mu_assert( "test_independent_matrix: assertion #3 failed", gsl_matrix_get( independent, 4, 5 )   ==43.1);
	mu_assert( "test_independent_matrix: assertion #4 failed", gsl_matrix_get( independent, 767, 14 )==0.315);

	cout<<"SUCCESS: test_independent_matrix"<<endl;

	//success
	return 0;
}


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_dependent_vector()
{
	//required before calling data file
	ddas_parse_scoping_file();

	//parse data file
	ddas_parse_data_file();

	//using 'master' populate 'dependent' vector
	ddas_populate_dependent_vector();

	//check that dependent vector is correct
	mu_assert( "test_dependent_vector: assertion #1 failed", gsl_vector_get( dependent, 0 )	==1.0);
	mu_assert( "test_dependent_vector: assertion #2 failed", gsl_vector_get( dependent, 2 )	==1.0);
	mu_assert( "test_dependent_vector: assertion #3 failed", gsl_vector_get( dependent, 5 )	==0.0);
	mu_assert( "test_dependent_vector: assertion #4 failed", gsl_vector_get( dependent, 767)==0.0);

	cout<<"SUCCESS: test_dependent_vector"<<endl;

	//success
	return 0;
}


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_scoping_file_parse()
{
	//parse scoping file
	ddas_parse_scoping_file();

	//check that variables are set correctly
	mu_assert("test_scoping_file_parse: assertion #1 failed", zscompare(starting_value, 1l)==0 );
	mu_assert("test_scoping_file_parse: assertion #2 failed", zscompare(ending_value, 65535l)==0 );
	mu_assert("test_scoping_file_parse: assertion #3 failed", results_to_return==10 );
	mu_assert("test_scoping_file_parse: assertion #4 failed", row_count==768 );
	mu_assert("test_scoping_file_parse: assertion #5 failed", independent_var_count==16 );
	mu_assert("test_scoping_file_parse: assertion #6 failed", dependent_var_count==1 );

	cout<<"SUCCESS: test_scoping_file_parse"<<endl;

	//success
	return 0;
};


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_state_file_parse()
{
	//alloc a new verylong
	verylong i = 0;

	//set 'i' based on state file
	ddas_parse_state_file( &i );

	//check that the result set is read in properly

	mu_assert("test_state_file_parse: assertion #4 failed", result_set->size==10 );
	mu_assert("test_state_file_parse: assertion #1 failed", zscompare(i, 60000l)==0 );
	mu_assert("test_state_file_parse: assertion #5 failed", result_set->result[8].sse == 137.435035 );
	mu_assert("test_state_file_parse: assertion #2 failed", zscompare(result_set->result[0].definition_number, 16346l)==0 );
	mu_assert("test_state_file_parse: assertion #3 failed", zscompare(result_set->result[9].definition_number, 39935l)==0 );	
	mu_assert("test_state_file_parse: assertion #5 failed", gsl_vector_get(result_set->result[9].regression_coefficients, 15) == 58874580371.506805 );
	
	//free memory
	zfree(&i);

	cout<<"SUCCESS: test_state_file_parse"<<endl;

	//success
	return 0;
};


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_LIP()
{
	verylong a=0;
	verylong b=0;
	verylong c=0;
	verylong d=0;
	verylong placeholder = 0;
	verylong remainder = 0;

	zintoz(5l, &a);
	zintoz(3l, &b);
	zintoz(2l, &c);
	zintoz(1l, &d);


	//5-3 = 2
	zsub(a,b, &placeholder);

	//2==2 as verylong? 
	mu_assert("test_LIP: assertion #1 failed", zcompare(placeholder, c)==0 );

	//2==2 as long?
	mu_assert("test_LIP: assertion #2 failed", zscompare(placeholder, 2l)==0 );

	//2<3 ?
	mu_assert("test_LIP: assertion #3 failed", zcompare(placeholder, b)==-1 );

	//3>2 ?
	mu_assert("test_LIP: assertion #4 failed", zcompare(b, placeholder)==1 );

	//1+2 = 3
	zadd(c,d,&placeholder);

	//3==3 as verylong?
	mu_assert("test_LIP: assertion #5 failed", zcompare(b, placeholder)==0 );

	//5 == 5 w/ conversion?
	mu_assert("test_LIP: assertion #6 failed", ztoint(a)==5l );

	//5^2 = 25
	zexp(a,c, &placeholder);

	//5^2 == 25 ?
	mu_assert("test_LIP: assertion #7 failed", ztoint(placeholder)==25l );

	//modulo using verylongs 25%3 
	zmod(placeholder, b, &placeholder);

	//25%3 == 1?
	mu_assert("test_LIP: assertion #8 failed", ztoint(placeholder)==1l );

	//reset placeholder = 25
	zintoz(25l, &placeholder);

	//modulo using longs
	mu_assert("test_LIP: assertion #9 failed", zsmod(placeholder, 3l)==1l );

	//reset placeholder = 25+7=32
	zsadd(placeholder, 7l, &placeholder);

	//placeholder == 32?
	mu_assert("test_LIP: assertion #10 failed", zscompare(placeholder, 32l)==0 );

	//log2(32)+1 == 5+1 ? (see lip documentation this is odd)
	mu_assert("test_LIP: assertion #11 failed", z2log(placeholder)==6l );

	//32 / 5 
	zdiv(placeholder, a, &placeholder, &remainder);

	//quotient == 6?
	mu_assert("test_LIP: assertion #12 failed", ztoint(placeholder)==6l );

	//remainder == 2?
	mu_assert("test_LIP: assertion #13 failed", ztoint(remainder)==2l );

	cout<<"SUCCESS: test_LIP"<<endl;

	//free memory
	zfree(&a);
	zfree(&b);
	zfree(&c);
	zfree(&d);
	zfree(&placeholder);
	zfree(&remainder);

	return 0;
};


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_result_file_write(){};


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_state_file_write(){};


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_verylong_percentages(){};


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* test_failure_1()
{
	mu_assert("test_failure_1 - assertion #1", 1==0);
	mu_assert("test_failure_1 - assertion #2", 1==0);

	return "";
}

static char* test_failure_2()
{
	mu_assert("test_failure_2 - assertion #1", 1==0);
	mu_assert("test_failure_2 - assertion #2", 1==0);

	return "";
}


/**
* @author Max Rupplin
* @date 5-18-08
* @return A char* stating the error that occurred
*
*
*/
static char* all_tests() 
{
	 mu_run_test(test_linearfit);
	 mu_run_test(test_relative_tolerance);
	 mu_run_test(test_data_file_parse);
	 mu_run_test(test_independent_matrix);
	 mu_run_test(test_dependent_vector);
	 mu_run_test(test_scoping_file_parse);
	 mu_run_test(test_state_file_parse);
	 mu_run_test(test_LIP);

	 mu_run_test(test_failure_1);
	 mu_run_test(test_failure_2);
	
     return 0;
}


int main(int argc, char **argv) 
{
	printf("\n*** UNCW DDAS TEST SUITE ***\n\n");

	char *result = all_tests();

	if (test_errors)
	{
		printf("%s\n", test_errors);
	}
	else printf("\n\n*** ALL %d TESTS PASSED ***\n", tests_run);

	//boinc_sleep(10); util.C
 
    return result != 0;
}
